package script;

import java.util.ArrayList;

import main.ErrorException;
import define.AttributeType;
import define.Common;
import define.Config;
import define.ErrorDefine;
import define.ExcelType;
import define.FunctionType;
import define.Keyword;
import define.VariableType;
import excelBase.BaseContent;
import excelBase.ExcelData;
import excelBase.ExcelFile;
import excelBase.Sheet;

public class Cell extends VariableContainer implements BaseExpress
{
	private Variable m_excel;
	private ArrayList<Variable> m_variableList;
	private ArrayList<Boolean> m_isStringList;
	private ArrayList<Attribute> m_attributes;

	private Script m_script;
	private int m_start;
	private int m_end;

	public Cell(Script script, int start, int end, VariableContainer father) throws ErrorException 
	{
		super(father, script.getGlobalVariables());
		m_script = script;
		m_start = start;
		m_end = end;
		m_excel = null;
		m_attributes = null;
		m_variableList = new ArrayList<>();
		m_isStringList = new ArrayList<>();
		make();
	}

	private void make() throws ErrorException 
	{
		int index = find(Keyword.E_CELL_EXCEL_KEY.getChar(), m_start + 1);
		int start = m_start + 1;
		if(index != Config.NO_FIND) 
		{
			m_excel = new Variable(m_script, start, index - 1);
			start = index + 1;
		}
		index = find(start);
		while(true)
		{
			if(index == Config.NO_FIND) 
			{
				if(start > m_end - 1)
				{
					return;
				}
				if(m_script.m_scriptCode[start] == Keyword.E_CELL_STRING_KEY.getChar())
				{
					++start;
					m_isStringList.add(true);
				}
				else
				{
					m_isStringList.add(false);
				}
				m_variableList.add(new Variable(m_script, start, m_end - 1));
				return;
			}
			else
			{
				if(start <= index - 1)
				{
					if(m_script.m_scriptCode[start] == Keyword.E_CELL_STRING_KEY.getChar())
					{
						++start;
						m_isStringList.add(true);
					}
					else
					{
						m_isStringList.add(false);
					}
					m_variableList.add(new Variable(m_script, start, index - 1));
				}
				start = index + 1;
				if(m_script.m_scriptCode[index] == Keyword.E_CELL_ATTR_KEY.getChar())
				{
					makeAttribute(start);
					return;
				}
			}
			index = find(start);
		}
	}
	
	private void makeAttribute(int start) throws ErrorException
	{
		m_attributes = new ArrayList<>();
		while(start < m_end) 
		{
			int index = find(Keyword.E_CELL_ATTR_KEY.getChar(), start);
			if(index != Config.NO_FIND) 
			{
				m_attributes.add(buildAttribute(start, index - 1));
				start = index + 1;
			} 
			else 
			{
				m_attributes.add(buildAttribute(start, m_end - 1));
				return;
			}
		}
	}

	private int find(int start) 
	{
		for(; start <= m_end; ++start) 
		{
			if(m_script.m_scriptCode[start] == Keyword.E_CELL_SPLIT_KEY.getChar()
					|| m_script.m_scriptCode[start] == Keyword.E_CELL_ATTR_KEY.getChar()
					&& m_script.m_scriptCode[start - 1] != Keyword.E_ESC_KEY.getCode()) 
			{
				return start;
			}
		}
		return Config.NO_FIND;
	}
	
	private int find(int aim, int start)
	{
		for(; start <= m_end; ++start) 
		{
			if(m_script.m_scriptCode[start] == aim
					&& m_script.m_scriptCode[start - 1] != Keyword.E_ESC_KEY.getCode()) 
			{
				return start;
			}
		}
		return Config.NO_FIND;
	}

	private Attribute buildAttribute(int start, int end) throws ErrorException 
	{
		String temp = "";
		while(start <= end && Common.isBlank(m_script.m_scriptCode[start]))
		{
			++start;
		}
		while(end >= start && Common.isBlank(m_script.m_scriptCode[end])) 
		{
			--end;
		}
		boolean isSplit = false;
		for(int i = start; i <= end; ++i) 
		{
			if(m_script.m_scriptCode[i] < 0) 
			{
				isSplit = true;
				break;
			}
			temp += (char) m_script.m_scriptCode[i];
		}
		if(!isSplit)
		{
			AttributeType at = AttributeType.hasAttribute(temp);
			if(at != null) 
			{
				return new Attribute(at, start);
			}
		}
		return makeSplit(start, end, start);
	}
	
	private Attribute makeSplit(int start, int end, int position) throws ErrorException
	{
		int split = Config.NO_FIND;
		for(int i = start; i <end; ++i)
		{
			if(m_script.m_scriptCode[i] == Config.ATTR_NUMBER_SPLIT)
			{
				split = i - 1;
				break;
			}
		}
		Integer i1 = 0;
		Integer i2 = 0;
		if(split == Config.NO_FIND)
		{
			String attr = Common.buildString(m_script.m_scriptCode, start, end);
			i1 = Common.isInt(attr);
			if(i1 != null)
			{
				return new Attribute(i1, -1, position);
			}
			AttributeType type = AttributeType.isSplitAttribute(attr);
			if(type == null)
			{
				throw new ErrorException(ErrorDefine.E_NO_ATTRIBUTE_ERROR, attr, position);
			}
			return new Attribute(type, null, true, position);
		}
		else
		{
			String temp = Common.buildString(m_script.m_scriptCode, start, split);
			++split;
			while (split <= end && m_script.m_scriptCode[split] == Config.ATTR_NUMBER_SPLIT)
			{
				++split;
			}
			i1 = Common.isInt(temp);
			if(i1 != null)
			{
				temp = Common.buildString(m_script.m_scriptCode, split, end);
				i2 = Common.isInt(temp);
				if(i2 == null)
				{
					throw new ErrorException(ErrorDefine.E_ATTRIBUTE_WRONG_ERROR, position);
				}
				return new Attribute(i1, i2, position);
			}
			AttributeType type = AttributeType.isSplitAttribute(temp);
			if(type == null)
			{
				throw new ErrorException(ErrorDefine.E_NO_ATTRIBUTE_ERROR, temp, position);
			}
			boolean isString = false;
			Variable var = null;
			if(m_script.m_scriptCode[split] == Keyword.E_CELL_STRING_KEY.getChar())
			{
				isString = true;
				var = new Variable(m_script, split + 1, end);
			}
			else
			{
				var = new Variable(m_script, split, end);
			}
			return new Attribute(type, var, isString, position);
		}
	}

	public StringBuffer getValue() throws ErrorException
	{
		ExcelFile excel = getExcel();
		int index = m_variableList.size();
		BaseContent base = getLastBase(excel);
		if(m_attributes == null)
		{
			Sheet sheet = getSheet();
			if(sheet.getExcelType() == ExcelType.E_TABLE_EXT)
			{
				for(; index < Config.TABLE_CELL_MAX_NUMBER; ++index)
				{
					base = base.getChild(0, m_start);
				}
			}
			return new StringBuffer(base.getName());
		}
		String out = base.getName();
		for(Attribute temp: m_attributes)
		{
			if(temp.getVariable() != null)
			{
				temp.getVariable().setStringValue(getVariableValue(temp.getVariable()));
			}
			out = base.getAttributeValue(temp, out);
		}
		return new StringBuffer(out);
	}
	
	public ExcelFile getExcel() throws ErrorException
	{
		if(m_excel != null)
		{
			return ExcelData.getExcelFile(m_excel.getValue().toString(), m_start);
		}
		else
		{
			return m_script.getExcel();
		}
	}
	
	private BaseContent getNext(BaseContent base, int index) throws ErrorException
	{
		String name = getVariableValue(m_variableList.get(index)).toString();
		if(m_isStringList.get(index))
		{
			return base.getChild(name, m_variableList.get(index).getStart());
		}
		Integer number = Common.isInt(name);
		if(number == null)
		{
			throw new ErrorException(ErrorDefine.E_VARIABLE_MUST_NUMBER_ERROR, name, m_variableList.get(index).getStart());
		}
		return base.getChild(number, m_variableList.get(index).getStart());
	}

	public VariableType getType()
	{
		return VariableType.E_CELL_VT;
	}

	public int getStart() 
	{
		return m_start;
	}
	
	public BaseContent getLastBase() throws ErrorException
	{
		ExcelFile excel = getExcel();
		return getLastBase(excel);
	}
	
	private BaseContent getLastBase(BaseContent base) throws ErrorException
	{
		int index = 0;
		for(int max = m_variableList.size(); index < max; ++index)
		{
			base = getNext(base, index);
		}
		return base;
	}

	public FunctionType getFunctionType() 
	{
		return null;
	}
	
	public Sheet getSheet() throws ErrorException
	{
		ExcelFile excel = getExcel();
		if(m_variableList.size() < 1)
		{
			return (Sheet)excel.getChild(0, m_start);
		}
		return (Sheet)getNext(excel, 0);
	}
	
	public String getSheetName() throws ErrorException
	{
		if(m_variableList.size() < 1)
		{
			throw new ErrorException(ErrorDefine.E_NOT_APPOINT_SHEET_ERROR, m_start);
		}
		return getVariableValue(m_variableList.get(0)).toString();
	}
}
